package cn.itcast.nems.order.util;

import cn.hutool.core.collection.CollectionUtil;
import cn.itcast.ic.common.exception.v2.BizExceptionProducer;
import cn.itcast.nems.base.dto.ClazzDTO;
import cn.itcast.nems.common.enumeration.NemsErrorBodyEnum;
import cn.itcast.nems.common.util.LocalDateTimeUtils;
import cn.itcast.nems.discounts.dao.dataobject.EmsDiscountsDO;
import cn.itcast.nems.journal.enumeration.JournalDiscountLineState;
import cn.itcast.nems.journal.enumeration.JournalDiscountState;
import cn.itcast.nems.manager.util.CustomBeanUtil;
import cn.itcast.nems.order.dao.dataobject.JournalDiscountDO;
import cn.itcast.nems.order.dao.entity.JournalDiscount;
import cn.itcast.nems.order.dao.entity.JournalDiscountLine;
import cn.itcast.nems.order.dto.JournalDiscountApiResDTO;
import cn.itcast.nems.order.dto.JournalDiscountDTO;
import cn.itcast.nems.order.enumeration.DiscountTypeEnum;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.lang.NonNull;
import org.springframework.lang.Nullable;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * 业务凭单SERVICE层工具类
 *
 * @author ChangLu.Gao
 * @version v1.0
 * @program nems-server
 * @description 业务凭单SERVICE层工具类
 * @date 2023/12/08 16:30
 */
public class JournalDiscountServiceUtil {
    /**
     * 保存标识
     */
    public final static String SAVE_FLAG = "SAVE";
    /**
     * 修改标识
     */
    public final static String MODIFY_FLAG = "MODIFY";
    /**
     * 删除标识
     */
    public final static String DELETE_FLAG = "DELETE";

    /**
     * 数据转换
     *
     * @param source 源数据
     * @return cn.itcast.nems.order.dto.JournalDiscountDTO
     * @author 高昌露
     * @since 2023/12/8 16:34
     */
    public static JournalDiscountDTO convert(@NonNull JournalDiscountDO source, @Nullable Map<Integer, EmsDiscountsDO> draftDiscountMap) {
        JournalDiscountDTO target = new JournalDiscountDTO();
        BeanUtils.copyProperties(source, target);
        target.setDiscountTypeDesc(DiscountTypeEnum.valueOf(source.getDiscountType()).getDesc());
        target.setStateDesc(JournalDiscountState.valueOf(source.getState()).getDesc());
        if (CollectionUtil.isNotEmpty(draftDiscountMap)) {
            EmsDiscountsDO discountsDO = draftDiscountMap.get(Integer.valueOf(target.getDiscountId()));
            if (null != discountsDO) {
                DiscountTypeEnum discountType = DiscountTypeEnum.getByValue(discountsDO.getDiscountsStatus());
                target.setDiscountType(discountType.name());
                target.setDiscountTypeDesc(discountType.getDesc());
                target.setDiscountName(discountsDO.getDiscountsName());
                target.setDiscountValue(getDiscountValue(discountType, discountsDO));
            }
        }
        return target;
    }

    /**
     * 根绝优惠配置获取优惠金额/折扣
     *
     * @param type        优惠类型
     * @param discountsDO 优惠配置
     * @return java.math.BigDecimal
     * @author 高昌露
     * @since 2023/12/9 14:50
     */
    public static BigDecimal getDiscountValue(@NonNull DiscountTypeEnum type,
                                              @NonNull EmsDiscountsDO discountsDO) {
        BigDecimal result = null;
        if (DiscountTypeEnum.DISCOUNT.equals(type)) {
            result = BigDecimal.TEN.multiply(BigDecimal.ONE.subtract(BigDecimal.valueOf(discountsDO.getDiscountsRate())));
        } else if (DiscountTypeEnum.CASH.equals(type)) {
            result = discountsDO.getDiscountsMoney();
        } else {
            BizExceptionProducer.throwProduce(NemsErrorBodyEnum.ENUM_ERROR, type.getDesc());
        }
        return result;
    }

    /**
     * 根据优惠配置, 解析优惠规则
     *
     * @param discountsDO 优惠配置
     * @return java.lang.String JSON字符串
     * @author 高昌露
     * @since 2023/12/9 14:58
     */
    public static String parseDiscountRule(@NonNull EmsDiscountsDO discountsDO) {
        JSONObject rule = new JSONObject();
        if (null != discountsDO.getClazzBeginDate()) {
            rule.put("clazzBeginDate", LocalDateTimeUtils.formatLocalDateTime(discountsDO.getClazzBeginDate()));
        } else {
            rule.put("clazzBeginDate", "1900-01-01 00:00:00");
        }
        if (null != discountsDO.getClazzEndDate()) {
            rule.put("clazzEndDate", LocalDateTimeUtils.formatLocalDateTime(discountsDO.getClazzEndDate()));
        } else {
            rule.put("clazzEndDate", "9999-12-31 23:59:59");
        }
        if (null != discountsDO.getFeeBeginDate()) {
            rule.put("feeBeginDate", LocalDateTimeUtils.formatLocalDateTime(discountsDO.getFeeBeginDate()));
        } else {
            rule.put("feeBeginDate", "1900-01-01 00:00:00");
        }
        if (null != discountsDO.getFeeEndDate()) {
            rule.put("feeEndDate", LocalDateTimeUtils.formatLocalDateTime(discountsDO.getFeeEndDate()));
        } else {
            rule.put("feeEndDate", "9999-12-31 23:59:59");
        }
        if (StringUtils.isNotBlank(discountsDO.getApplyScopeJsonArrayStr())) {
            JSONArray applyClazzType = JSONArray.parseArray(discountsDO.getApplyScopeJsonArrayStr());
            rule.put("applyClazzType", applyClazzType);
        }
        return rule.toJSONString();
    }

    /**
     * 数据转换
     *
     * @param source   源数据
     * @param lineList 优惠单行数据集合
     * @return cn.itcast.nems.order.dto.JournalDiscountDTO
     * @author 高昌露
     * @since 2023/12/8 16:34
     */
    public static JournalDiscountDTO convert(@NonNull JournalDiscount source,
                                             @Nullable List<JournalDiscountLine> lineList
    ) {
        JournalDiscountDTO target = new JournalDiscountDTO();
        BeanUtils.copyProperties(source, target);
        target.setDiscountTypeDesc(DiscountTypeEnum.valueOf(source.getDiscountType()).getDesc());
        target.setStateDesc(JournalDiscountState.valueOf(source.getState()).getDesc());
        parseDiscountRule(source.getDiscountRule(), target);
        if (CollectionUtil.isNotEmpty(lineList)) {
            target.setLineList(
                    lineList.stream()
                            .map(o -> CustomBeanUtil.copyProperties(o, new JournalDiscountDTO.JournalDiscountLineDTO()))
                            .collect(Collectors.toList())
            );
        }
        return target;
    }

    /**
     * 解析优惠格则
     *
     * @param discountRuleJsonStr json字符串格式的优惠规则
     * @param result              映射的目标类
     * @author 高昌露
     * @since 2023/12/8 16:59
     */
    private static void parseDiscountRule(String discountRuleJsonStr, JournalDiscountDTO result) {
        JSONObject discountRule = JSON.parseObject(discountRuleJsonStr);
        // 班级开课起始时间
        String clazzBeginDateStr = discountRule.getString("clazzBeginDate");
        // 班级开课结束时间
        String clazzEndDate = discountRule.getString("clazzEndDate");
        // 交费开始时间
        String feeBeginDate = discountRule.getString("feeBeginDate");
        // 交费结束时间
        String feeEndDate = discountRule.getString("feeEndDate");
        // 适用的班级类型范围: jsonArray格式的字符串
        JSONArray applyClazzType = JSONArray.parseArray(discountRule.getString("applyClazzType"));
        result.setClazzBeginDate(LocalDateTimeUtils.parse(clazzBeginDateStr));
        result.setClazzEndDate(LocalDateTimeUtils.parse(clazzEndDate));
        result.setFeeBeginDate(LocalDateTimeUtils.parse(feeBeginDate));
        result.setFeeEndDate(LocalDateTimeUtils.parse(feeEndDate));
        result.setApplyClazzType(applyClazzType);
    }

    /**
     * 构建存储/编辑的行数据
     *
     * @param journalDiscountId             优惠单头ID
     * @param reqLineList                   请求的行数据
     * @param existsJournalDiscountLineList 此优惠单已经关联的行数据
     * @return SAVE:需要保存的数据, MODIFY:需要按照ID修改的数据, DELETE:需要按照ID删除的数据集合
     * @author 高昌露
     * @since 2023/12/9 15:58
     */
    public static Map<String, List<JournalDiscountLine>> buildLineList(@NonNull String journalDiscountId,
                                                                       @NonNull final List<JournalDiscountDTO.JournalDiscountLineDTO> reqLineList,
                                                                       @Nullable final List<JournalDiscountLine> existsJournalDiscountLineList
    ) {
        Map<String, List<JournalDiscountLine>> result = new HashMap<>();
        result.put(SAVE_FLAG, new ArrayList<>());
        result.put(MODIFY_FLAG, new ArrayList<>());
        result.put(DELETE_FLAG, new ArrayList<>());
        List<String> modifyIds = new ArrayList<>();
        if (CollectionUtil.isNotEmpty(reqLineList)) {
            for (JournalDiscountDTO.JournalDiscountLineDTO journalDiscountLineDTO : reqLineList) {
                if (journalDiscountLineDTO.getId() != null) {
                    modifyIds.add(journalDiscountLineDTO.getId());
                    result.get(MODIFY_FLAG).add(CustomBeanUtil.copyProperties(journalDiscountLineDTO, new JournalDiscountLine()));
                } else {
                    JournalDiscountLine r = CustomBeanUtil.copyProperties(journalDiscountLineDTO, new JournalDiscountLine());
                    r.setJournalDiscountId(journalDiscountId);
                    r.setState(JournalDiscountLineState.PENDING.name());
                    result.get(SAVE_FLAG).add(r);
                }
            }
        }
        if (CollectionUtil.isNotEmpty(existsJournalDiscountLineList)) {
            result.get(DELETE_FLAG)
                    .addAll(
                            existsJournalDiscountLineList
                                    .stream()
                                    .filter(o -> !modifyIds.contains(o.getId()))
                                    .toList()
                    );
        }
        return result;
    }

    /**
     * 在之间
     *
     * @param dateTime  指定时间
     * @param startTime 开始时间
     * @param endTime   结束时间
     * @return boolean
     * @author 高昌露
     * @since 2023/12/10 13:56
     */
    public static boolean atBetween(LocalDateTime dateTime, LocalDateTime startTime, LocalDateTime endTime) {
        return dateTime.isBefore(endTime) && dateTime.isAfter(startTime);
    }

    /**
     * 校验目标班级是否在适用班级类型范围内
     *
     * @param target         目标班级
     * @param applyClazzType 适用班级类型范围
     * @return boolean true: 符合要求
     * @author 高昌露
     * @since 2023/12/10 14:19
     */
    public static boolean checkClazzScope(ClazzDTO target, JSONArray applyClazzType) {
        boolean result = false;
        outer:
        for (int i = 0; i < applyClazzType.size(); i++) {
            JSONObject school = applyClazzType.getJSONObject(i);
            String schoolCode = school.getString("code");
            if (target.getSchoolId().equals(schoolCode)) {
                JSONArray teachingModelArray = school.getJSONArray("children");
                for (int j = 0; j < teachingModelArray.size(); j++) {
                    JSONObject teachingModel = teachingModelArray.getJSONObject(j);
                    String teachingModelCode = teachingModel.getString("code");
                    if (teachingModelCode.equals(target.getModelId())) {
                        JSONArray clazzTypeArray = teachingModel.getJSONArray("children");
                        for (int m = 0; m < clazzTypeArray.size(); m++) {
                            JSONObject clazzType = clazzTypeArray.getJSONObject(m);
                            String clazzTypeCode = clazzType.getString("code");
                            if (clazzTypeCode.equals(target.getClassTypeId())) {
                                result = true;
                                break outer;
                            }
                        }
                    }
                }
            }
        }
        return result;
    }

    /**
     * 对外API参数转化<br>
     * 优惠类型为折扣时, discountValue有特殊处理
     *
     * @param source 源数据
     * @return java.util.List<cn.itcast.nems.order.dto.JournalDiscountApiResDTO>
     * @author 高昌露
     * @since 2023/12/10 15:54
     */
    public static @NonNull
    List<JournalDiscountApiResDTO> convert(@NonNull JournalDiscountDTO source) {
        return source.getLineList()
                .stream()
                .map(o -> {
                    JournalDiscountApiResDTO target = new JournalDiscountApiResDTO();
                    target.setDiscountId(source.getDiscountId());
                    target.setDiscountName(source.getDiscountName());
                    target.setDiscountType(source.getDiscountType());
                    target.setDiscountTypeDesc(source.getDiscountTypeDesc());
                    if (DiscountTypeEnum.DISCOUNT.equals(DiscountTypeEnum.valueOf(target.getDiscountType()))) {
                        target.setDiscountValue(BigDecimal.ONE.subtract(source.getDiscountValue().divide(BigDecimal.TEN, 2, RoundingMode.HALF_UP)));
                    } else {
                        target.setDiscountValue(source.getDiscountValue());
                    }
                    target.setCustomerId(o.getCustomerId());
                    target.setStudentId(o.getStudentId());
                    target.setJournalDiscountLineId(o.getId());
                    target.setRemark(source.getRemark());
                    return target;
                })
                .toList();
    }
}
